package com.xingkeduo.util.tool;

/**
 * 主键生成工具类
 * 
 * @author GJ
 * @time 2016年10月7日 下午6:32:57
 * @description
 */
public class IdentityUtil {
	public enum Worker {
		DEFAULT(3l, 9l), ORDER(2l, 2l), USER(3l, 9l);

		private long id;
		private long idBits;

		private Worker(long id, long idBits) {
			this.id = id;
			this.idBits = idBits;

			long maxWorkerId = -1L ^ (-1L << idBits);
			if (id > maxWorkerId || id < 0) {
				throw new IllegalArgumentException(
						String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
			}
		}

		public long getId() {
			return this.id;
		}

		public long getIdBits() {
			return idBits;
		}
	}

	private static final long twepoch = 1545109931576L;
	private static final long sequenceBits = 2L;
	private static final long sequenceMask = -1L ^ (-1L << sequenceBits);
	private static long sequence = 0L;
	private static long lastTimestamp = -1L;

	/**
	 * 获得id
	 * 
	 * @author GJ
	 * @time 2018年12月26日 下午1:28:15
	 * @param worker
	 * @return
	 */
	public synchronized static long nextId(Worker worker) {
		long workerId = worker.getId();
		long timestamp = timeGen();
		if (timestamp < lastTimestamp) {
			throw new RuntimeException(String.format(
					"Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
		}
		if (lastTimestamp == timestamp) {
			sequence = (sequence + 1) & sequenceMask;
			if (sequence == 0) {
				timestamp = tilNextMillis(lastTimestamp);
			}
		} else {
			sequence = 0L;
		}

		lastTimestamp = timestamp;

		long timestampLeftShift = sequenceBits + worker.getIdBits();
		return ((timestamp - twepoch) << timestampLeftShift) | (workerId << sequenceBits) | sequence;
	}

	private static long tilNextMillis(long lastTimestamp) {
		long timestamp = timeGen();
		while (timestamp <= lastTimestamp) {
			timestamp = timeGen();
		}
		return timestamp;
	}

	private static long timeGen() {
		return System.currentTimeMillis();
	}

	/**
	 * 测试
	 * 
	 * @author GJ
	 * @time 2018年12月26日 下午1:30:03
	 * @param args
	 */
	public static void main(String[] args) {
		for (int i = 0; i < 20; i++) {
			long id1 = IdentityUtil.nextId(Worker.DEFAULT);
			long id2 = IdentityUtil.nextId(Worker.ORDER);
			System.out.print("id1==============" + id1);
			System.out.println("===============id2==============" + id2);
		}
	}
}